﻿using Citi.ActionItem.Domain.Utils;
using Citi.ActionItem.WebUI.Models;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Providers.Entities;

namespace Citi.ActionItem.WebUI.Controllers
{
    public class ControllerBase : Helpers.Session
    {
        internal string MemoryUsagePath
        {
            get
            {
                var dllFolder = Path.GetDirectoryName(DllFolder);
                return Path.Combine(dllFolder, "MemoryUsage");
            }
        }

        internal string ServerName
        {
            get
            {
                return DllFolder.Split(new[] { "/", @"\\", @"\" }, StringSplitOptions.RemoveEmptyEntries).First();
            }
        }

        internal string DllFolder
        {
            get { return ConfigurationManager.AppSettings["DllFolder"]; }
        }

        internal string NetworkUser
        {
            get { return ConfigurationManager.AppSettings["NetworkUser"]; }
        }

        internal string NetWorkPassword
        {
            get { return ConfigurationManager.AppSettings["NetWorkPassword"]; }
        }
    }

    public class ControllerBase<T, TEntity> : ControllerBase
        where T : ModelBase<T, TEntity>, new()
        where TEntity : GeneralAction, new()
    {



        #region Propriedades

        private GeneralAction domain;

        private GeneralAction Domain
        {
            get { return domain ?? (domain = (GeneralAction)Activator.CreateInstance(typeof(TEntity))); }
        }

        public IEnumerable<GeneralAction> Domains { get; set; }
        public T Model { get; set; }

        private List<T> models;
        public List<T> Models
        {
            get
            {
                if (models == null || !models.Any())
                    GetAllActive();

                return models;
            }
            set { models = value; }
        }

        #endregion

        #region Actions

        #region Manter

        public virtual ActionResult ActionBase(long? id, bool partial = false)
        {
            if (id == null)
                return CreateBase(partial);

            var method = GetType().GetMethod("GetToEdit");
            var model = method.Invoke(this, new object[] { (int)id, true });

            if (partial)
                return PartialView(model);

            return View(model);
        }

        [HttpPost]
        public virtual ActionResult ActionBase(T model, bool partial = false)
        {
            try
            {
                if (!Validate(model))
                {
                    ExecuteFillViewBagsMethod(model);
                    if (partial)
                        return PartialView();

                    return View();
                }

                //model.Ativo = true;

                if (model.Id == 0)
                {
                    var dominioCriar = model.CreateDomain();
                    dominioCriar.Save(dominioCriar);
                    Model = model.CreateModel(dominioCriar);
                }
                else
                {
                    SaveEdition(model, true);
                    Model = model;
                }

                if (partial)
                    return RedirectToAction("Index", new { partial = true });

                return RedirectToAction("Index");
            }
            catch
            {
                ExecuteFillViewBagsMethod(model);

                if (partial)
                    return PartialView();

                return View();
            }
        }

        #endregion

        #region Criar

        public virtual ActionResult CreateBase(bool partial = false)
        {
            ExecuteFillViewBagsMethod(new T());

            if (partial)
                return PartialView();

            return View();
        }

        [HttpPost]
        public virtual ActionResult CreateBase(T model)
        {
            return ActionBase(model);
        }

        #endregion

        #region Index

        public virtual ActionResult IndexBase(bool partial = false)
        {
            GetAllActive();

            if (partial)
                return PartialView(Models);

            return View(Models);
        }

        private void GetAllActive()
        {
            var method = ObterMetodoGenerico("SelectAllActive");

            var dominios = Domains = (IEnumerable<GeneralAction>)method.Invoke(Domain, null);
            var instanciaModel = (T)Activator.CreateInstance(typeof(T));
            Models = (List<T>)instanciaModel.CreateModel(dominios);
        }

        #endregion

        #region Detalhes

        public virtual ActionResult DetailsBase(long id)
        {
            var method = ObterMetodoGenerico("SelectById");

            var dominioBanco = (GeneralAction)method.Invoke(Domain, new object[] { id });
            var instanciaModel = (T)Activator.CreateInstance(typeof(T));
            Model = instanciaModel.CreateModel(dominioBanco);

            return View(Model);
        }

        #endregion

        #region Delete

        public virtual ActionResult DeleteBase(long id, bool partial = false)
        {
            var method = ObterMetodoGenerico("SelectById");

            var dominioBanco = (GeneralAction)method.Invoke(Domain, new object[] { id });
            dominioBanco.Inative(dominioBanco);

            if (partial)
                return RedirectToAction("Index", new { partial = true });

            return RedirectToAction("Index");
        }

        #endregion

        #endregion


        private MethodInfo ObterMetodoGenerico(string nomeMetodo)
        {
            var method = Domain.GetType().GetMethod(nomeMetodo);
            return method.MakeGenericMethod(Domain.GetType());
        }

        private void ExecuteFillViewBagsMethod(T model)
        {
            var metodo = GetType().GetMethod("FillViewBag") ?? GetType().GetMethod("FillViewBags");

            if (metodo == null) return;

            var parametros = metodo.GetParameters();
            var valoresParametros = new List<object>();

            foreach (var parametro in parametros)
            {
                var propriedade = typeof(T).GetProperty(string.Concat(parametro.Name.Substring(0, 1).ToUpper(),
                                                                       parametro.Name.Substring(1,
                                                                                                parametro.Name.Length -
                                                                                                1)));

                if (propriedade == null)
                {
                    valoresParametros.Add(null);
                    continue;
                }

                valoresParametros.Add(propriedade.GetValue(model, null));
            }

            metodo.Invoke(this, valoresParametros.ToArray());
        }

        #region Validação

        public void CorrigirMensagemErro()
        {
            var errosVs =
                ModelState.Values.Where(e => e.Errors.Count > 0 && e.Errors[0].ErrorMessage.StartsWith("The value '"));

            foreach (var erro in errosVs)
            {
                var mensagem = erro.Errors[0].ErrorMessage;

                erro.Errors[0] = new ModelError(mensagem);
            }
        }

        public bool Validate(ModelBase<T, TEntity> model)
        {
            CorrigirMensagemErro();

            //return ModelState.IsValid && ValidarModel(model);
            return ValidateModel(model);
        }

        public bool ValidateModel(ModelBase<T, TEntity> model)
        {
            var method = model.GetType().GetMethod("Validate");

            if (method == null) return true;

            var validacao = (Dictionary<string, string>)method.Invoke(model, new[] { model });

            if (validacao != null && validacao.Count > 0)
            {
                foreach (var keyPair in validacao)
                    ModelState.AddModelError(keyPair.Key, keyPair.Value);

                return false;
            }

            return true;
        }

        #endregion

        #region Edição

        public T SaveEdition(ModelBase<T, TEntity> model, bool usarIdSessao = false)
        {
            var nomeSessao = string.Concat(model.GetType().Name.Replace("Model", string.Empty),
                                           usarIdSessao ? model.Id.ToString() : string.Empty);

            var dominio =
                (TEntity)Session[nomeSessao] ??
                ((GeneralAction)Activator.CreateInstance(typeof(TEntity))).SelectById<TEntity>(model.Id);

            //model.Ativo = true;
            dominio = model.CreateDomain(dominio);
            dominio.Save(dominio);

            Session[nomeSessao] = null;

            return model.CreateModel(dominio);
        }

        public T GetToEdit(int id, bool usarIdSessao = false)
        {
            var dominio = (TEntity)Activator.CreateInstance(typeof(TEntity));
            dominio = dominio.SelectById<TEntity>(id);
            Session[string.Concat(dominio.GetType().Name, usarIdSessao ? id.ToString() : string.Empty)] = dominio;

            var model = (ModelBase<T, TEntity>)Activator.CreateInstance(typeof(T));

            model = model.CreateModel(dominio);

            ExecuteFillViewBagsMethod((T)model);

            return (T)model;
        }

        #endregion
    }
}
